#include "Context.hh"
#include "common/stl.h"
#include "tools/constants.hh"
#include "media/Sound.hh"
#include "input/Input.hh"


Context::Context() :
	_glMajorVersion (-1),
	_glMinorVersion (-1),
	_soundContext (0),
	_soundDevice (0)
{
}

Context::~Context()
{
    // close window and destroy OpenGL context
    glfwCloseWindow();
    glfwTerminate();

    // Destroy OpenAL Context && close OpenAL device
    alcMakeContextCurrent(NULL);
    alcDestroyContext(_soundContext);
    alcCloseDevice(_soundDevice);

	#ifdef ZERMINGORE
		if (_glMajorVersion < 4)
			std::cerr << "_____ WARNING: performed *WITHOUT* optirun©" << std::endl;
	#endif
} // auto destroy window && OpenGL contexts

#ifdef DEBUG
	void Context::check(bool full)
	{
		if (full)
		{
			std::cout << "VENDOR " << glGetString(GL_VENDOR) << std::endl;
			std::cout << "RENDERER " << glGetString(GL_RENDERER) << std::endl;
			std::cout << "VERSION " << glGetString(GL_VERSION) << std::endl;
		}

		glGetIntegerv(GL_MAJOR_VERSION, &_glMajorVersion);
		glGetIntegerv(GL_MINOR_VERSION, &_glMinorVersion);
		std::cout << "Using OpenGL " << _glMajorVersion << "." << _glMinorVersion << std::endl;

		int glfw[3] = {-1, -1, -1}; // initializing version and revision to -1
		// get these informations
		glfwGetVersion(&glfw[0], &glfw[1], &glfw[2]);
		std::cout << "Using GLFW version " << glfw[0] << "." << glfw[1]
				<< " revision " << glfw[2] << std::endl;

		// we could darken unreachable options
		#ifdef ZERMINGORE
			if (_glMajorVersion < 4)
				std::cerr << "_____ WARNING: running *WITHOUT* optirun©" << std::endl;
		#endif

        minimalOpenGL(4);
	}

    void Context::minimalOpenGL(unsigned int version)
    {
	  if (_glMajorVersion < (int) version)
        {
            std::cerr << "openGL version *NOT* supported, need " << version
                   << ".0 or higher" << std::endl
                   << "please consider updating your drivers" << std::endl;
            #ifndef OS_WINDOWS
                std::cerr << "if your're using Optimus© technology, run *WITH* your graphic card"
                   << std::endl
                   << "for example, run like this: $>optirun rods"
                   << std::endl;
            #endif
            std::exit(EXIT_FAILURE);
         }
    }
#endif



void Context::init()
{
    // Initialize GLFW
    if (!glfwInit())
    {
        std::cerr << "Unable to initialize GLFW" << std::endl;
        std::exit(EXIT_FAILURE);
    }

    // Open a window and create its OpenGL context
	#ifdef DEBUG // need to try stencil && alphanum sizes values
    // create a 640 x 480 window
    if (!glfwOpenWindow(0, 0, 0, 0, 0, 0, 32, 0, GLFW_WINDOW))
	#else
    // open a full screen window which dims closest match 1920 x 1080
    if (!glfwOpenWindow(1920, 1080, 0, 0, 0, 0, 32, 0, GLFW_FULLSCREEN))
	#endif
    {
        std::cerr << "Unable to open a GLFW window" << std::endl;
        glfwTerminate();
        std::exit(EXIT_FAILURE);
    }

    // GLEW initialization
    GLenum initialisationGLEW(glewInit());

    if (initialisationGLEW != GLEW_OK)
    {
        std::cerr << "GLEW initialization error : "
                << glewGetErrorString(initialisationGLEW) << std::endl;
        glfwTerminate();
        std::exit(EXIT_FAILURE);
    }

    #ifdef DEBUG
        check(); // checking OpenGL version
    #endif

    this->alterWindow();

    // OpenAL initialization
    if (!(_soundDevice = alcOpenDevice(NULL)))
    {
      std::cerr << "No sound device" << std::endl;
      std::exit(-2);
    }

    _soundContext = alcCreateContext(_soundDevice, NULL);
    if (!alcMakeContextCurrent(_soundContext))
    {
      std::cerr << "No sound context" << std::endl;
      std::exit(-2);
    }
}


void Context::alterWindow()
{
    glfwSetWindowTitle("RODS");

    // V-Sync - ATI drivers parameters override this
    // 0 : no buffer refresh speed limits
    // 1 : wait for the screen to refresh
    glfwSwapInterval(1);

    // set OpenGL version
    glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 4);
    glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 2);
    // let the system choose profile
    // glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // Register event callbacks
    glfwSetKeyCallback(Input::keyboardCallback);
    glfwSetMouseButtonCallback(Input::mouseButtonsCallback);
    glfwSetMousePosCallback(Input::mousePositionCallback);

    // NOTE: glfw auto enable double buffer
    glfwEnable(GLFW_KEY_REPEAT);
    #ifndef DEBUG
    	// disabling Windows© key, Alt-Tab, Ctrl-C, ...
    	glfwDisable(GLFW_SYSTEM_KEYS);
        glfwDisable(GLFW_MOUSE_CURSOR);
		glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4); // anti-alias 4x
    #endif

    int width, height;
    glfwGetWindowSize(&width, &height);
    glfwSetMousePos(width / 2, height / 2);
}


void Context::setGLFeatures()
{
    // Cull triangles which normal is not towards the camera
    glEnable(GL_CULL_FACE); // => do not draw not displayed models
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS); // Accept fragments if they're close from the camera
}
